package com.shisan.tingshu.album.service.impl;

import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import com.shisan.tingshu.album.config.VodProperties;
import com.shisan.tingshu.album.service.VodService;
import com.shisan.tingshu.common.execption.ShisanException;
import com.shisan.tingshu.common.util.UploadFileUtil;
import com.shisan.tingshu.vo.album.TrackMediaInfoVo;
import com.tencentcloudapi.common.AbstractModel;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.vod.v20180717.VodClient;
import com.tencentcloudapi.vod.v20180717.models.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.Arrays;
import java.util.HashMap;


@Service
@Slf4j
public class VodServiceImpl implements VodService {

    @Autowired
    private VodProperties vodProperties;

    /**
     * 上传媒体文件至腾讯云VOD中
     *
     * @param file
     * @return
     */
    @Override
    public HashMap<String, Object> uploadTack(MultipartFile file) {
        // 构建VOD的客户端对象
        VodUploadClient vodClient = new VodUploadClient(vodProperties.getSecretId(), vodProperties.getSecretKey());

        // 后端是无法通过file对象获取到路径的，在之前上传图片至minio时，是通过流的方式上传的
        // 而vod是没有这种通过流的方式上传的，所以需要将文件上传到临时路径中
        // UploadFileUtil 是自定义的，用于将文件上传到临时路径
        String mediaFileUrl = UploadFileUtil.uploadTempPath(vodProperties.getTempPath(), file);

        // 构建上传请求对象
        VodUploadRequest request = new VodUploadRequest();
        request.setMediaFilePath(mediaFileUrl);
        request.setSubAppId(Long.parseLong(vodProperties.getAppId().toString())); // 指定要将声音上传到哪个应用中
        request.setStorageRegion(vodProperties.getRegion());
        request.setConcurrentUploadNumber(5); // 指定上传并发数。当上传的文件较大时，会将文件分片上传，此处指的是由多少个线程同时上传文件分片

        // 开始上传
        try {
            VodUploadResponse response = vodClient.upload(vodProperties.getRegion(), request);
            log.info("Upload FileId = {}", response.getFileId());
            HashMap<String, Object> map = new HashMap<>();
            map.put("mediaFileId", response.getFileId());
            map.put("mediaUrl", response.getMediaUrl());
            return map;
        } catch (Exception e) {
            // 业务方进行异常处理
            log.error("上传音频至腾讯云VOD失败，原因：{}", e.getMessage());
            throw new ShisanException(201, "上传音频至腾讯云VOD失败");
        }
    }

    /**
     * 根据媒体文件ID获取媒体信息
     *
     * @param mediaFileId
     * @return
     */
    @Override
    public TrackMediaInfoVo getMediaInfo(String mediaFileId) {

        // 重试配置
        final int MAX_RETRY_COUNT = 5; // 最大重试次数
        final long RETRY_INTERVAL_MS = 2000; // 重试间隔(毫秒)
        int retryCount = 0;
        // 创建实例认证对象
        Credential cred = new Credential(vodProperties.getSecretId(), vodProperties.getSecretKey());
        // 创建查询媒体信息的vod客户端对象
        VodClient client = new VodClient(cred, vodProperties.getRegion());
        // 创建Request对象
        DescribeMediaInfosRequest req = new DescribeMediaInfosRequest();
        String[] fileIds1 = {mediaFileId};
        req.setFileIds(fileIds1);
        req.setSubAppId(Long.parseLong(vodProperties.getAppId().toString()));

        while (retryCount < MAX_RETRY_COUNT) {
            try {
                log.info("尝试获取媒体信息，FileId: {} (尝试次数: {}/{})",
                        mediaFileId, retryCount + 1, MAX_RETRY_COUNT);
                // 发起请求，并得到响应对象
                DescribeMediaInfosResponse resp = client.DescribeMediaInfos(req);
                // 输出json格式的字符串回包
                log.info("VOD响应: {}", AbstractModel.toJsonString(resp));

                // 检查媒体文件是否存在
                if (resp.getNotExistFileIdSet() != null &&
                        Arrays.asList(resp.getNotExistFileIdSet()).contains(mediaFileId)) {
                    log.info("文件尚未就绪，等待重试...");
                    retryCount++;
                    if (retryCount < MAX_RETRY_COUNT) {
                        Thread.sleep(RETRY_INTERVAL_MS);
                    }
                    continue;
                }

                // 处理有效响应
                MediaInfo[] mediaInfoSet = resp.getMediaInfoSet();
                if (mediaInfoSet != null && mediaInfoSet.length != 0) {
                    MediaInfo mediaInfo = mediaInfoSet[0];
                    MediaBasicInfo basicInfo = mediaInfo.getBasicInfo();
                    MediaMetaData metaData = mediaInfo.getMetaData();
                    TrackMediaInfoVo trackMediaInfoVo = new TrackMediaInfoVo();
                    trackMediaInfoVo.setSize(metaData.getSize());
                    trackMediaInfoVo.setDuration(metaData.getAudioDuration());
                    trackMediaInfoVo.setMediaUrl(basicInfo.getMediaUrl());
                    trackMediaInfoVo.setType(basicInfo.getType());
                    log.info("根据媒体文件ID获取媒体信息：{}", trackMediaInfoVo);
                    return trackMediaInfoVo;
                }
            } catch (TencentCloudSDKException e) {
                log.error("查询媒体信息失败: {}", e.getMessage());
            } catch (Exception e) {
                log.error("获取媒体信息发生未知异常：{}", e.getMessage());
                break;
            }
        }

        return null;
    }

    /**
     * 删除vod中的声音（同时删除磁盘中临时目录的文件）
     * @param mediaFileId
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeMediaFile(String mediaFileId) {
        try {
            // 创建实例认证对象
            Credential cred = new Credential(vodProperties.getSecretId(), vodProperties.getSecretKey());
            // 创建查询媒体信息的vod客户端对象
            VodClient client = new VodClient(cred, vodProperties.getRegion());
            // 创建删除对象
            DeleteMediaRequest req = new DeleteMediaRequest();
            req.setFileId(mediaFileId);
            req.setSubAppId(Long.parseLong(vodProperties.getAppId().toString()));
            // 发起请求，并得到响应对象
            DeleteMediaResponse resp = client.DeleteMedia(req);
            // 输出json格式的字符串回包
            log.info("VOD响应: {}", AbstractModel.toJsonString(resp));
        } catch (TencentCloudSDKException e) {
            log.error("删除媒体信息失败: {}", e.getMessage());
            throw new ShisanException(201, "删除媒体信息失败");
        }
    }
}
